[Python] Boto3における例外クラスの同一性について
こんにちは。サービスグループの武田です。
Boto3を利用した処理の例外処理を書いていて少しハマったので共有します。
さていきなりですが、次のコードの実行結果を予想できるでしょうか。四択問題です。
import boto3 c1 = boto3.client("iam") c2 = boto3.client("iam") print(c1.exceptions.NoSuchEntityException == c2.exceptions.NoSuchEntityException) s = boto3.Session() c3 = s.client("iam") print(c1.exceptions.NoSuchEntityException == c3.exceptions.NoSuchEntityException)
True/True と予想した人が多いでしょうか?少なくとも私はそう思っていました。
正解は True/False です。なんとclient
を生成する元のオブジェクト(boto3やSession)が異なると例外クラスも別物となるようです。そのため次のようなコードは意図した挙動となりません。例外をキャッチできないのです。
import boto3 def aws_op(): s = boto3.Session() iam = s.client("iam") iam.get_role(RoleName="not-exists-role") iam = boto3.client("iam") try: aws_op() except iam.exceptions.NoSuchEntityException as e: print(e)
そのため、発生する例外の親クラスであるClientError
をキャッチするよう、例外処理の部分を次のように書き換えることで意図した動作となります。具体的にどの例外が投げられたのかはCode
を見ることで分岐させます。
try: aws_op() except iam.exceptions.ClientError as e: if e.response["Error"]["Code"] == "NoSuchEntity": print(e) else: raise e
または、例外クラスはbotocore
パッケージから参照しても問題ありません。ドキュメントなどではこちらの書き方をよく見ます。ただVSCodeの拡張でPylanceというLinterがあるのですが、この書き方をするとエラーが出てちょっと困ります(コメントで抑制は可能)。
try: aws_op() except botocore.exceptions.ClientError as e: if e.response["Error"]["Code"] == "NoSuchEntity": print(e) else: raise e
ちなみに次のように同一オブジェクトを利用できるのであれば、わざわざCode
による分岐は不要です。スッキリ書けてよいですね。
import boto3 def aws_op(): s = boto3.Session() iam = s.client("iam") try: iam.get_role(RoleName="not-exists-role") except iam.exceptions.NoSuchEntityException as e: print(e) aws_op()
まとめ
例外を投げる処理と同じ場所でキャッチするのであれば、直接その例外を書いてキャッチする書き方が好みです。一方で、離れた場所でキャッチする場合はClientError
でのキャッチが必要です。またいつでも同じ書き方をしたいということであれば、ClientError
で書いておけば問題ありません。
それでは、よいAWSライフを。